//////////////////////////////////////////////////////////////////////////
// $Id: MFile.cpp,v 1.1 2009/04/03 12:46:10 sunjun Exp $
//////////////////////////////////////////////////////////////////////////

#include "Local.h"
#include "zlib/zlib.h"

//////////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////////

NAMESPACE_BEGIN

//////////////////////////////////////////////////////////////////////////

	// the dynamic creator
	MIMPLEMENT_DYNCREATOR(MFile)

	// dispatch map
	MBEGIN_DISPATCH_MAP(MFile, MObject)
		MDISPATCH_FUNCTION(MFile,	"open",		open,	(DARG2<BOOL,CHAR*,U32>(&DARGFN2)))
		MDISPATCH_FUNCTION(MFile,	"read",		read,	(DARG1<U32,MData*>(&DARGFN1)))
		MDISPATCH_FUNCTION(MFile,	"write",	write,	(DARG1<U32,MData*>(&DARGFN1)))
		MDISPATCH_FUNCTION(MFile,	"seek",		seek,	(DARG2<BOOL,I32,U32>(&DARGFN2)))
		MDISPATCH_FUNCTION(MFile,	"tell",		tell,	(DARG0<U32>(&DARGFN0)))
		MDISPATCH_FUNCTION(MFile,	"close",	close,	(DARG0<VOID>(&DARGFN0)))
	MEND_DISPATCH_MAP()

	//////////////////////////////////////////////////////////////////////////


	MFile::MFile()
	{
		GUARD(MFile::MFile);

		mFile = NULL;
		mOption = 0;
		mCompress = FALSE;
		mPos = 0;

		UNGUARD;
	}

	MFile::~MFile()
	{
		GUARD(MFile::~MFile);

		if( mFile ) close();

		UNGUARD;
	}
	
	//////////////////////////////////////////////////////////////////////////

	/*
	====================
	open
	====================
	*/
	INLINE BOOL	MFile::open( const CHAR *name, U32 option )
	{
		GUARD(MFile::open);

		// close the old file
		close();

		// store the option of the file
		mOption = option;
		switch( mOption )
		{
		case 0:
			{
				// open the file for reading
				FILE *file = fopen( name, "rb" );
				if( file == NULL )return FALSE;

				// read the compress mark : 4 byte
				U32 mark = 0;
				if( fread( &mark, 1, sizeof(U32), file ) == sizeof(U32) )
				{
					if( mark == MAKE_FOURCC('g','z','i','p') ) // 0x70697a6e
					{
						// set the compress state
						mCompress = TRUE;

						// read the size of the uncompress buffer
						U32 uncompress_size = 0;
						CHECK( fread( &uncompress_size, 1, sizeof(U32), file ) == sizeof(U32) );
						mBuffer.resize(uncompress_size);

						// get the size of buffer
						U32 pos = ftell( file );
						CHECK( fseek( file, 0, SEEK_END ) == 0 );
						U32 compress_size = ftell( file ) - pos;
						CHECK( fseek( file, pos, SEEK_SET ) == 0 );

						// read all of the compress buffer and uncompress it
						std::vector<U8>compress_data(compress_size);
						CHECK( fread( &compress_data[0], 1, compress_size, file ) == compress_size );						

						// uncompress it
						CHECK( uncompress( (Bytef*)&mBuffer[0], (uLongf*)&uncompress_size, (Bytef*)&compress_data[0], (uLong)compress_size ) == Z_OK );
					}
					else
					{
						// reset the file pointer
						CHECK( fseek( file, 0, SEEK_SET ) == 0 );
					}
				}
				else
				{
					// reset the file pointer
					CHECK( fseek( file, 0, SEEK_SET ) == 0 );
				}

				// store the file handle
				mFile = (HANDLE)file;

				// set the position
				mPos = 0;
			}
			break;
		case 1:	// open the file for writing
			{
				FILE *file = fopen( name, "wb" );
				if( file == NULL )return FALSE;

				// store the state
				mFile = (HANDLE)file;
				mCompress = FALSE;
				mPos = 0;
			}
			break;
		case 2:	// open the file for writing compress buffer
			{
				FILE *file = fopen( name, "wb" );
				if( file == NULL )return FALSE;

				// store the state
				mFile = (HANDLE)file;
				mCompress = TRUE;
				mPos = 0;

				// get the size of buffer
				U32 pos = ftell( file );
				CHECK( fseek( file, 0, SEEK_END ) == 0 );
				U32 size = ftell( file ) - pos;
				CHECK( fseek( file, pos, SEEK_SET ) == 0 );

				// read the all of the file buffer
				mBuffer.resize(size);				
				CHECK( fread( &mBuffer[0], 1, size, file ) == size );

				// reset the file 
				CHECK( fseek( file, 0, SEEK_SET ) == 0 );
			}
			break;
		default:
			return FALSE;
			break;
		}

		return TRUE;

		UNGUARD;
	}

	/*
	====================
	read
	====================
	*/
	INLINE U32 MFile::read( MData* data )
	{
		GUARD(MFile::read);		

		CHECK(mFile);

		CHECK(data);
		U32 size = data->size();
		VOID* buffer = (VOID*)data->get();

		if( mCompress )
		{
			U32 real_size = (size <= mBuffer.size() - mPos ? size : mBuffer.size() - mPos);
			memcpy( buffer, &mBuffer[mPos] , real_size );
			mPos += real_size;
			return real_size;
		}
		
		return fread( buffer, 1, size, (FILE*)mFile );

		UNGUARD;
	}

	/*
	====================
	write
	====================
	*/
	INLINE U32 MFile::write( const MData* data )
	{
		GUARD(MFile::write);

		CHECK(mFile);

		if(!(mOption==1||mOption==2)) return 0;	

		CHECK(data);
		U32 size = data->size();
		const VOID* buffer = data->get();

		if( mCompress )
		{
			if( mPos + size > mBuffer.size() )
			{
				mBuffer.resize(mPos+size);
			}
			memcpy( &mBuffer[mPos], buffer, size );
			mPos += size;
			return size;
		}

		return fwrite( buffer, 1, size, (FILE*)mFile );

		UNGUARD;
	}

	/*
	====================
	seek
	====================
	*/
	INLINE BOOL	MFile::seek( I32 offset, U32 origin )
	{
		GUARD(MFile::seek);

		CHECK(mFile);

		if( mCompress ) 
		{
			switch( origin )
			{
			case 0:
				{
					I32 start_pos = 0;
					if( start_pos + offset >= 0 && start_pos + offset < mBuffer.size() )
					{
						mPos = start_pos + offset;
						return TRUE;
					}
					else
					{
						return FALSE;
					}
				}			
				break;
			case 1:
				{
					I32 start_pos = mPos;
					if( start_pos + offset >= 0 && start_pos + offset < mBuffer.size() )
					{
						mPos = start_pos + offset;
						return TRUE;
					}
					else
					{
						return FALSE;
					}
				}
				break;
			case 2:
				{
					I32 start_pos = mBuffer.size() - 1;
					if( start_pos + offset >= 0 && start_pos + offset < mBuffer.size() )
					{
						mPos = start_pos + offset;
						return TRUE;
					}
					else
					{
						return FALSE;
					}
				}
				break;
			default:
				return FALSE;
				break;
			}
		}
		else
		{
			if( fseek( (FILE*)mFile, offset, origin ) == 0 )
			{
				return TRUE;
			}
			else
			{
				return FALSE;
			}
		}

		return FALSE;

		UNGUARD;
	}

	/*
	====================
	tell
	====================
	*/
	INLINE U32 MFile::tell()
	{
		GUARD(MFile::tell);

		CHECK(mFile);
		if( mCompress ) return mPos;
		return ftell( (FILE*)mFile );

		UNGUARD;
	}

	/*
	====================
	close
	====================
	*/
	INLINE VOID	MFile::close()
	{
		GUARD(MFile::close);

		if( mFile == NULL ) return;

		// write the compress buffer
		if( mOption == 2 )
		{
			// compress the buffer
			U32 uncompress_size = mBuffer.size();
			U32 compress_size = mBuffer.size();
			std::vector<U8>compress_data(compress_size);
			CHECK(compress( (Bytef*)&compress_data[0], (uLongf *)&compress_size, (const Bytef*)&mBuffer[0], (uLong)mBuffer.size() ) == Z_OK );		

			// seek the file pointer to the start
			CHECK( fseek( (FILE*)mFile, 0, SEEK_SET ) == 0 );

			// write the zip header
			U32 mark = MAKE_FOURCC('g','z','i','p');
			CHECK(fwrite( &mark, 1, sizeof(mark), (FILE*)mFile ) == sizeof(mark) );

			// write the size of the compress buffer
			CHECK(fwrite( &uncompress_size, 1, sizeof(uncompress_size), (FILE*)mFile ) == sizeof(uncompress_size) );

			// write the compress buffer
			CHECK(fwrite( &compress_data[0], 1, compress_size, (FILE*)mFile ) == compress_size );
		}

		// close the file
		fclose((FILE*)mFile);
		mFile = NULL;
		mOption = 0;
		mCompress = FALSE;
		mPos = 0;

		UNGUARD;
	}

//////////////////////////////////////////////////////////////////////////

NAMESPACE_END

//////////////////////////////////////////////////////////////////////////

